package com.cy;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * 基于此类处理限流、熔断事件触发时出现的异常
 * 说明：
 * 1）系统有默认处理限流、熔断事件触发的异常的处理器（DefaultBlockExceptionHandler）
 * 2）当默认处理异常的方式不满足业务需求时，我们需要自己定义异常处理器
 * 3）自定义异常处理器时需要实现BlockExcetionHandler接口，
 *  并将异常处理器对象交给spring管理
 * 4）自定义异常处理器以后，默认异常处理器失效
 *   SpringCloud-Sentinel系统底层配置类SentinelWebConfiguration中配置
 *   （@ConditionalOnMissingBean）
 *   当spring容器没有自定义异常处理类时再调用默认底层配置的异常处理器DefaultBlockExceptionHandler
 */

@Component
public class ServiceBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       BlockException e) throws Exception {

        //Note：先设置 响应数据的字符集 和 响应客户端的响应类型和字符集 ，再去获取输出流（否则设置不生效）
        //1.先设置响应参数 -> 2.获取输出流对象，准备响应数据 -> 3.写出输出流对象 -> 4.刷新输出流推送至客服端

        //设置响应数据的编码
        response.setCharacterEncoding("utf-8");

        //告诉客服端要响应的数据类型以及客户端以什么编码呈现数据
        response.setContentType("text/html;character=utf-8");

        //通过响应对象获取响应输出流
        PrintWriter pw = response.getWriter();

        //通过创建map对象来存储响应数据
        Map<String,Object> map = new HashMap<>();

        //根据异常类型进行信息封装
        if (e instanceof DegradeException){
            map.put("status",601);
            map.put("message","服务被熔断了！");
        }else if (e instanceof FlowException) {
            map.put("status", 602);
            map.put("message","服务被限流了！");
        }else if(e instanceof SystemBlockException) {
            map.put("status", 603);
            map.put("message","系统繁忙！");
        }else if(e instanceof AuthorityException) {
            map.put("status", 604);
            map.put("message","授权限制！");
        }else {
            map.put("status",605);
            map.put("message","Blocked by Sentinel (flow limiting)");
        }

        //将map对象转化为json格式的字符串
        String jsonStr = new ObjectMapper().writeValueAsString(map);

        //通过输出流对象向客户端写出json数据
        pw.println(jsonStr);

        //推送输出流并关闭流对象
        pw.flush();

    }
}
